#!/usr/bin/python

"""Test the mutex behavior of a circuit.

To test the basic wait/acquire/release functionality, simply run this script:

$ ./testmutex.py

This will hold the mutex with a thread that sleeps for a few seconds, and
simultaneously start another thread that blocks until the first is complete.

To test the ability of the mutex to clean itself up, simply kill this process
and check that the next time through it has the right stats for holders and
waiters (they will be displayed automatically).

To test reset functionality (the ability to kill all waiters and holders)
you can do this from a bash prompt:

$ ((sleep 4; ./testmutex.py -r)&); ./testmutex.py -n 5
"""

from __future__ import division, print_function

import argparse
import sys
import threading
import time

from lib import circuit
from lib import ripple

def make_holder(mutex, sleep=0):
  """Create a run function that attempts to obtain a mutex."""
  def runhold():
    name = threading.current_thread().name
    def do():
      print("Thread {!r} obtained lock: Sleeping {}".format(name, sleep))
      time.sleep(sleep)

    val = mutex.acquire_and_do(f=do)
    print("Thread {!r} acquisition response: {!r}".format(name, val))
    print("Thread {!r} finished: {} holding, {} waiting".format(
      name, *mutex.stat()))
  return runhold


def main():
  parser = argparse.ArgumentParser()
  parser.add_argument("-r", "--reset",
                      help="reset mutex before running tests",
                      action="store_true", default=False)
  parser.add_argument("-n", "--numwait",
                      help="number of threads to contend for the first holder",
                      default=1, type=int)
  args = parser.parse_args()

  # Create a new mutex (new name and everything).
  mutex = ripple.Mutex(slab=circuit.local_lang(), name="m")

  if args.reset:
    print("resetting")
    mutex.reset()
    return

  threads = [threading.Thread(name="sleepy", target=make_holder(mutex, 3))]
  for i in xrange(args.numwait):
    threads.append(threading.Thread(name="quick{}".format(i),
                                    target=make_holder(mutex.copy(), 1)))

  for t in threads:
    t.daemon = True
    t.start()

  for t in threads:
    t.join(15)

  sys.stdout.flush()
  print("Finished with all threads")

if __name__ == '__main__':
  main()
